/*
 * Copyright (c) 2009-2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_DEBUG_H_INCLUDED_
#define ALKES_DEBUG_H_INCLUDED_

#include "alkes/internal.h"
#include <stdarg.h>

#ifndef LOG_LABEL
#define LOG_LABEL   0
#endif

#ifndef ENABLE_TRACE
#define ENABLE_TRACE 0
#endif

#ifndef ENABLE_LOGV
#ifdef NDEBUG
#define ENABLE_LOGV 0
#else
#define ENABLE_LOGV 1
#endif
#endif

#ifndef ENABLE_PROFILE
#define ENABLE_PROFILE 0
#endif

#ifndef ENABLE_LOG_MACRO
#define ENABLE_LOG_MACRO 1
#endif

#define AL_ERROR(...)   _alkes_dprintf(ERROR, __VA_ARGS__)
#define AL_WARN(...)    _alkes_dprintf(WARN, __VA_ARGS__)
#define AL_NOTICE(...)  _alkes_dprintf(NOTICE, __VA_ARGS__)
#define AL_INFO(...)    _alkes_dprintf(INFO, __VA_ARGS__)

#if !defined(__GNUC__)
#define AL_NO_SUPPORT(fmt, ...) _alkes_dprintf(ERROR, fmt " IS NOT SUPPORTED!", __VA_ARGS__)
#else
#define AL_NO_SUPPORT(fmt, ...) _alkes_dprintf(ERROR, fmt " IS NOT SUPPORTED!", ## __VA_ARGS__)
#endif



#define AL_ERROR_IF(cond, ...) \
    ((cond) ? AL_ERROR(__VA_ARGS__) : ((void)0))

#define AL_WARN_IF(cond, ...) \
    ((cond) ? AL_WARN(__VA_ARGS__) : ((void)0))

#define AL_NOTICE_IF(cond, ...) \
    ((cond) ? AL_NOTICE(__VA_ARGS__) : ((void)0))

#define AL_INFO_IF(cond, ...) \
    ((cond) ? AL_INFO(__VA_ARGS__) : ((void)0))


#if ENABLE_TRACE
#define AL_TRACE()          _alkes_dprintf(DEBUG, "%s(%d)", __FILE__, __LINE__)
#define AL_TRACE_API(exp)   do{exp;_alkes_dprintf(LOG, #exp);}while(0)
#define AL_TRACE_IF(cond)   ((cond) ? AL_TRACE() : ((void)0))
#else
#define AL_TRACE()          do{}while(0)
#define AL_TRACE_API(exp)   exp
#define AL_TRACE_IF(cond)   do{}while(0)
#endif

#if ENABLE_LOGV
#define AL_VERBOSE(...)     _alkes_dprintf(VERBOSE, __VA_ARGS__)
#define AL_VERBOSE_IF(cond, ...) \
    ((cond) ? AL_VERBOSE(__VA_ARGS__) : ((void)0))
#else
#define AL_VERBOSE(...)             do{}while(0)
#define AL_VERBOSE_IF(cond, ...)    do{}while(0)
#endif


#if defined(ALKES_DEBUG)

#define AL_DBG(...)         _alkes_dprintf(DEBUG, __VA_ARGS__)
#define AL_ASSERT(exp)      AL_VERIFY(exp)
#define AL_ASSERT_BREAK()   alkes_debug_break()
#define AL_PRE_COND_BREAK() alkes_debug_break()

#else

#define AL_DBG(...)         do{}while(0)
#define AL_ASSERT(exp)      do{}while(0)
#define AL_ASSERT_BREAK()   alkes_abort()
#define AL_PRE_COND_BREAK() do{}while(0)

#endif

#define AL_VERIFY(exp)          \
    do {                        \
        if (!(exp)) {           \
            AL_ERROR("assert: %s(%d): \"" #exp "\"", __FILE__, __LINE__); \
            AL_ASSERT_BREAK();  \
        }                       \
    } while(0)

#define AL_PRE_COND(exp, retval)                            \
    do {                                                    \
        if (!(exp)) {                                       \
            AL_ERROR("pre-cond: %s(%d): \"" #exp "\"", __FUNC_NAME__, __LINE__);  \
            AL_PRE_COND_BREAK();                            \
            return retval;                                  \
        }                                                   \
    } while (0)

typedef int (*alkes_error_callback)(uint32_t level, const char* label, const char *format, va_list arg);

AL_EXPORT_API void alkes_dprintf(uint32_t level, const char* label, const char* fmt, ...);
AL_EXPORT_API uint32_t alkes_set_error_level(uint32_t new_level);
AL_EXPORT_API uint32_t alkes_get_error_level();
AL_EXPORT_API alkes_error_callback alkes_set_error_callback(alkes_error_callback new_callback);
AL_EXPORT_API alkes_error_callback alkes_get_error_callback();
AL_EXPORT_API int alkes_format_log(char* buffer, int buflen, uint32_t level, const char* label, const char* fmt, va_list arg);

#if ENABLE_LOG_MACRO
#define _alkes_dprintf(level, ...)  alkes_dprintf(AL_ERRLV_##level, LOG_LABEL, __VA_ARGS__)
#else
#define _alkes_dprintf(level, ...)  ((void)0)
#endif


#define AL_ERRLV_ERROR      (0x01)
#define AL_ERRLV_WARN       (0x02)
#define AL_ERRLV_NOTICE     (0x04)
#define AL_ERRLV_INFO       (0x08)
#define AL_ERRLV_DEBUG      (0x10)
#define AL_ERRLV_VERBOSE    (0x20)
#define AL_ERRLV_PROFILE    (0x40)

#define AL_ERRLV_ALL        ((uint32_t)0xffffffff)


// shortcut
#ifndef LOGE
#define LOGE    AL_ERROR
#endif
#ifndef LOGE_IF
#define LOGE_IF AL_ERROR_IF
#endif
#ifndef LOGW
#define LOGW    AL_WARN
#endif
#ifndef LOGW_IF
#define LOGW_IF AL_WARN_IF
#endif
#ifndef LOGI
#define LOGI    AL_INFO
#endif
#ifndef LOGI_IF
#define LOGI_IF AL_INFO_IF
#endif
#ifndef LOGD
#define LOGD    AL_DBG
#endif
#ifndef LOGD_IF
#define LOGD_IF AL_DBG_IF
#endif
#ifndef LOGV
#define LOGV    AL_VERBOSE
#endif
#ifndef LOGV_IF
#define LOGV_IF AL_VERBOSE_IF
#endif
#ifndef TRACE
#define TRACE   AL_TRACE
#endif
#ifndef TRACE_IF
#define TRACE_IF AL_TRACE_IF
#endif


/* Profile
*/
#if ENABLE_PROFILE
#define PROF_BEGIN(...) \
    alkes_dprintf(AL_ERRLV_PROFILE, LOG_LABEL, "prof-start: " __VA_ARGS__)
#define PROF_END(...)   \
    alkes_dprintf(AL_ERRLV_PROFILE, LOG_LABEL, "prof-end: " __VA_ARGS__)
#define PROF_CHECK(...) \
    alkes_dprintf(AL_ERRLV_PROFILE, LOG_LABEL, "prof-check: " __VA_ARGS__)
#define PROF_CALL(exp)  \
    PROF_BEGIN(#exp);exp;PROF_END(#exp)
#else
#define PROF_BEGIN(...) ((void)0)
#define PROF_END(...)   ((void)0)
#define PROF_CHECK(...) ((void)0)
#define PROF_CALL(exp)  exp
#endif

#define PROF_BEGIN_IF(cond, ...) \
    ((cond) ? PROF_BEGIN(__VA_ARGS__) : ((void)0))

#define PROF_END_IF(cond, ...)   \
    ((cond) ? PROF_END(__VA_ARGS__) : ((void)0))

#define PROF_CHECK_IF(cond, ...) \
    ((cond) ? PROF_CHECK(__VA_ARGS__) : ((void)0))

#define PROF_CALL_IF(cond, exp) \
    PROF_BEGIN_IF(cond, #exp);exp;PROF_END_IF(cond, #exp)

#endif
